using System;
using System.IO;

namespace Taste {

  public class TC {

    // opcodes
    public const int ADD   = 0;
    public const int SUB   = 1;
    public const int MUL   = 2;
    public const int DIVI  = 3;
    public const int EQU   = 4;
    public const int LSS   = 5;
    public const int GTR   = 6;
    public const int LOAD  = 7;
    public const int LIT   = 8;
    public const int STO   = 9;
    public const int CALL  = 10;
    public const int RET   = 11;
    public const int RES   = 12;
    public const int JMP   = 13;
    public const int FJMP  = 14;
    public const int HALTc = 15;
    public const int NEG   = 16;
    public const int READ  = 17;
    public const int WRITE = 18;

    public static int progStart;  // address of first instruction of main program
    public static int pc;         // program counter
    public const int memSize = 15000;
    public const int stackSize = 1000;
    static byte[] code = new byte[memSize];
    static bool generatingCode = true;

    // data for Interpret
    static int[] stack = new int[stackSize];
    static int top;   // top of stack
    static int bp;    // base pointer

    public static void Emit (int op) {
      if (generatingCode) {
        if (pc >= memSize - 4) {Parser.SemError(9); generatingCode = false;}
        else code[pc++] = (byte) op;
      }
    }

    public static void Emit2 (int op, int val) {
      Emit(op); Emit(val >> 8); Emit(val);
    }

    public static void Emit3 (int op, int level, int val) {
      Emit(op); Emit(level); Emit(val >> 8); Emit(val);
    }

    public static void Fixup (int adr) {
      if (generatingCode) {
      code[adr] = (byte)(pc >> 8); code[adr+1] = (byte)pc;
      }
    }

    static int Next () {
      return code[pc++];
    }

    static private int Next2 () {
      int x, y;
      x = (sbyte)code[pc++]; y = code[pc++];
      return (x << 8) + y;
    }

    static int Int (bool b) {
      if (b) return 1; else return 0;
    }

    static void Push (int val) {
      stack[top++] = val;
    }

    static int Pop() {
      return stack[--top];
    }

    static int Up (int level) {
      int b = bp;
      while (level > 0) { b = stack[b]; level--; }
      return b;
    }

    static int ReadInt(TextReader input) {
      int ch, sign;
      try {
        do {ch = input.Read();} while (!(ch >= '0' && ch <= '9' || ch == '-'));
        if (ch == '-') {sign = -1; ch = input.Read();} else sign = 1;
        int n = 0;
        while (ch >= '0' && ch <= '9') {
          n = 10 * n + (ch - '0');
          ch = input.Read();
        }
        return n * sign;
      }
      catch (IOException) {
        Console.WriteLine("Error in data file");
        return 0;
      }
    }

    public static void Interpret () {
      int val, a, lev;
      TextReader input = Console.In;
      TextWriter output = Console.Out;
      string FileName;
      Console.Write("Input file (ENTER for keyboard) ? ");
      FileName = Console.ReadLine();
      if (FileName.Length != 0) {
        try {
          input = new StreamReader(new FileStream(FileName, FileMode.Open));
        }
        catch (Exception) {
          Console.WriteLine(FileName + " not found - input from keyboard");
          input = Console.In;
        }
      }
      Console.Write("Output file (ENTER for screen) ? ");
      FileName = Console.ReadLine();
      if (FileName.Length != 0) {
        try {
          output = new StreamWriter(new FileStream(FileName, FileMode.Create));
        }
        catch (Exception) {
          Console.WriteLine(FileName + " not created - output to screen");
          output = Console.Out;
        }
      }
      pc = progStart; bp = 0; top = 3; val = 0;
      for (;;)
        switch (Next()) {
          case LOAD:  lev = Next(); a = Next2(); Push(stack[Up(lev)+a]); break;
          case LIT:   Push(Next2()); break;
          case STO:   lev = Next(); a = Next2(); stack[Up(lev)+a] = Pop(); break;
          case ADD:   val = Pop(); Push(Pop() + val); break;
          case SUB:   val = Pop(); Push(Pop() - val); break;
          case DIVI:  val = Pop();
                      if (val != 0) { Push(Pop() / val); break; }
                      else {
                        Console.WriteLine("Divide by zero");
                        output.Close(); System.Environment.Exit(0); break;
                      }
          case MUL:   val = Pop(); Push(Pop() * val); break;
          case EQU:   val = Pop(); Push(Int(Pop() == val)); break;
          case LSS:   val = Pop(); Push(Int(Pop() < val)); break;
          case GTR:   val = Pop(); Push(Int(Pop() > val)); break;
          case CALL:  Push(Up(Next())); Push(bp); Push(pc+2);
                      pc = Next2(); bp = top - 3; break;
          case RET:   top = bp; bp = stack[top+1]; pc = stack[top+2]; break;
          case RES:   top = top + Next2(); break;
          case JMP:   pc = Next2(); break;
          case FJMP:  a = Next2(); if (Pop() == 0) pc = a; break;
          case HALTc: output.Close(); return;
          case NEG:   Push(-Pop()); break;
          case READ:  lev = Next(); a = Next2();
                      stack[Up(lev)+a] = ReadInt(input); break;
          case WRITE: output.WriteLine(Pop()); break;
          default:    Console.WriteLine("Illegal Instruction"); output.Close();
                      System.Environment.Exit(0); break;
        }

    }

    public static void Init () {
      pc = 1;
    }

  } // end TC

} // end namespace

